package search.service.search;

import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.index.query.*;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;


import org.springframework.stereotype.Component;
import search.config.AnalyzerNames;
import search.config.SearchParameters;
import common.dao.elastic.ElasticSearchDao;
import search.model.query.ComplexParameter;
import common.config.IndexTypeNames;
import common.helper.json.JsonFormatter;
import common.helper.serialization.FormatOfDate;
import search.service.analysis.EncodeDecode;

import javax.inject.Inject;

import java.util.ArrayList;
import java.util.List;

@Component
public class ComplexSearchService_V1 {
	final Logger logger = LoggerFactory.getLogger(ComplexSearchService_V1.class);

	private final ObjectMapper mapper;
	private final ElasticSearchDao elasticsearchDao;
    
	@Inject
	public ComplexSearchService_V1(
			ElasticSearchDao elasticsearchDao ,
			ObjectMapper mapper
	) {
		this.mapper = mapper;
		this.elasticsearchDao = elasticsearchDao;
	}


	//*******************************************//
	/*  <complexsearch>
	* complex search with Java
	* use ComplexParameter to transport params
	*/
	/***********************************************
	 *
	 * @param param
	 * @return
	 */
	public String complexSearch(ComplexParameter param) {

		logger.debug("complexSearch , param = \n {} ", param.toString());

		//****************************************************************************//
		/*
		   * <query>
		   * Query 部分
		   * 把query部分组合起来
		 */
		BoolQueryBuilder query = QueryBuilders.boolQuery();

		//****************************************//
		/*

		 * 处理有多个搜索字段时，这些字段的组合关系
		 *
		 *
		 * 使得参数全为空时,结果为空
		 * 因为只有 fulltext 的 MatchAllQuery,不满足最少两个 should
		 * must和should: must是否计入should?
		 */
		/*
		Integer shouldCount = 0;
		Integer shouldBase = 0;
		if(! (param.getFulltext() != null && !param.getFulltext().equals("")) ){
			shouldBase = 1; //fulltext
			if(param.getAllField() != null && !param.getAllField().equals("")){
				shouldBase += 1; //allField;
				//shouldBase += 2; //Title Content
			}
		}
		else{
			shouldBase = 3;  //querystring \ allfield \ fulltext
			//shouldBase += 2; //Title Content
		}
		*/
		/*
		 * 不要加参数个数了，因为媒资的构造本来就不是很合理
		 */
		//shouldCount = shouldBase + (param.getParamCount() - 1);
		//shouldCount = shouldBase;
		//query.minimumNumberShouldMatch(shouldCount);





		//******************************//
		/*
		 * <levels>
		 */
		//Set Levels
		Boolean isProgram = false , isSequence = false, isScene = false, isShot = false;
		List<String> levels = new ArrayList<String>();
		logger.debug("param.levels.size = {} \n" , param.getLevels().size());
		if(param.getLevels().size() == 0){
			isProgram = isSequence = isScene = isShot = true;
			levels.add(IndexTypeNames.MediaDB.ProgramSetName);
			levels.add(IndexTypeNames.MediaDB.SequenceSetName);
			levels.add(IndexTypeNames.MediaDB.SceneSetName);
			levels.add(IndexTypeNames.MediaDB.ShotSetName);
		}
		if(param.getLevels().contains( IndexTypeNames.MediaDB.ProgramSetName )) {
			isProgram = true;
			levels.add(IndexTypeNames.MediaDB.ProgramSetName);
		}
		if(param.getLevels().contains( IndexTypeNames.MediaDB.SequenceSetName )){
			isSequence = true;
			levels.add(IndexTypeNames.MediaDB.SequenceSetName);
		}
		if(param.getLevels().contains( IndexTypeNames.MediaDB.SceneSetName )){
			isScene = true;
			levels.add(IndexTypeNames.MediaDB.SceneSetName);
		}
		if(param.getLevels().contains( IndexTypeNames.MediaDB.ShotSetName )){
			isShot = true;
			levels.add(IndexTypeNames.MediaDB.ShotSetName);
		}


		//******************************//
		/*
		 * <minimum>
		 * 所有查询字段构成了 allField
		 * 限制 查询的allFiels 和 文档的_all 字段的匹配百分比
		 * 这一字段不使用了语言本体扩展，使用最大分词
		 */
		if(param.getAllField() != null && !param.getAllField().equals("")){
			MatchQueryBuilder base = QueryBuilders.matchQuery(
					"_all",
					param.getAllField()
			);
			/*
			* 参考 ： https://www.elastic.co/guide/en/elasticsearch/reference/1.4/query-dsl-minimum-should-match.html
			 */
			String minimum = param.getMinimum();
			base.minimumShouldMatch(minimum)
					.analyzer(AnalyzerNames.analyzers_v2[0])
					.boost(AnalyzerNames.boosting_v2[0]);

			query.must(
					base
							.boost(SearchParameters.Boosting.allfield)
			);
		}

		//******************************//



		//****************************************//
		/*
		 * <fulltext>
		 * <querystring>
		 * what the difference with matchAllQuery?
		 * well, such as bool condition + - 
		 * "must" 必须满足
		 */
		/*
		if(param.getFulltext()!= null && !param.getFulltext().equals("") ){
			BoolQueryBuilder multi  = QueryBuilders.boolQuery();
			for(int i = 0 ; i < AnalyzerNames.counter_v2; i++){
				BoolQueryBuilder wrapper = QueryBuilders.boolQuery();
				SimpleQueryStringBuilder base = 
						QueryBuilders.simpleQueryStringQuery(
								param.getFulltext()
								)
								.field("_all");
				multi.should(
						wrapper.should(
							base.analyzer(AnalyzerNames.analyzers_v2[i])
							)
						.boost(AnalyzerNames.boosting_v2[i])
						);
			}
			//包装一层， 否则simpleQueryStringQuery无法boost
			query.must(
					multi
					.boost(Boosting.querystring)
					);
		}
		*/


		//******************************//
		/*
		 * <fulltext>
		 * 没有针对编目层次区分
		 * 如果需要，则为每个编目层次定义一个 _all
		 */
		BoolQueryBuilder fulltextQ = QueryBuilders.boolQuery();
		if(param.getFulltext() == null || param.getFulltext().equals("")) {
			fulltextQ.must(
					QueryBuilders.matchAllQuery()
					);
		}
		else{
			//_all field search
			BoolQueryBuilder multi = QueryBuilders.boolQuery();
			for(int i = 0; i < AnalyzerNames.counter_v2 ; i ++){
				MatchQueryBuilder base = QueryBuilders.matchQuery(
						"_all" , 
						param.getFulltext()
						);
				multi.should(
						base
						.analyzer(AnalyzerNames.analyzers_v2[i])
						.boost(AnalyzerNames.boosting_v2[i])
						);
			}
			//fulltextQ.should(multi);
			fulltextQ = multi;
		}
		query.must(
				fulltextQ.
				boost(SearchParameters.Boosting.fulltext)
				);


		//****************************//
		/*
		 * <pinyin>
		 * 针对 _all 字段的pinyin搜索
		 * _all.pinyin
		 *
		 */
		if(param.getPinyin() != null && !param.getPinyin().equals("") ) {

			MatchQueryBuilder subquery = QueryBuilders.matchQuery(
					"SearchAllField.pinyin",
					param.getPinyin()
			);
			query.must(
					subquery
							.minimumShouldMatch("100%")
							.analyzer("ik_smart_pinyin")
							.boost(2)
			);
		}


		//****************************//
		/*
		 * 此处可定义多个检索字段
		 * 类似图书馆的高级检索
		 * 可以自定义与或非逻辑
		 * 可以针对一些预先选定的字段进行限制
		 *
		 * 这些查询域共同组成一个大的 “多域全文” 字段集合 的布尔查询
		 * 这些字段之间的组合关系可以选择  与或非
		 *
		 *
		 * 这个布尔查询和其他固定的检索条件是 And 的关系
		 */
		//*****************************//
				/*
		 * <title>
		 */
		// Begin Title
		//if(param.getTitle() != null && !param.getTitle().equals("")){
		if(false){
			//logger.debug("Searching Title ... {}",param.getTitle());
			BoolQueryBuilder titleQ = QueryBuilders.boolQuery();

			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();

				MatchQueryBuilder title = QueryBuilders.matchQuery(
						"Metadata.Program.SearchTitle",
						param.getTitle()
				);
				multi.should(
						title
								.analyzer("ik_max_word")
								.boost(3)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getTitle(),
							"Metadata.Program.Title.ProperTitle^3.0f",
							"Metadata.Program.Title.ParallelProperTitle^3.0f",
							"Metadata.Program.Title.SubordinateTitle^2.0f",
							"Metadata.Program.Title.AlternativeProperTitle^2.0f",
							"Metadata.Program.Title.TitleDescription",
							"Metadata.Program.Title.SeriesTitle",
							"Metadata.Program.Title.ParallelSeriesTitle"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				titleQ.should(
						multi.boost(2)
				);
			}
			if(isSequence){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder title = QueryBuilders.matchQuery(
						"Metadata.Sequence.SearchTitle",
						param.getTitle()
				);
				multi.should(
						title
								.analyzer("ik_max_word")
								.boost(3)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getTitle(),
							"Metadata.Sequence.Title.ProperTitle^3.0f",
							"Metadata.Sequence.Title.ParallelProperTitle^3.0f",
							"Metadata.Sequence.Title.SeriesTitle"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				titleQ.should(
						multi.boost(2)
				);
			}
			if(isScene) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getTitle(),
							"Metadata.Scene.Title.SceneTitle"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				titleQ.should(
						multi.boost(1)
				);
			}
			if(isShot) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getTitle(),
							"Metadata.Shot.Title.ShotTitle"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				titleQ.should(
						multi.boost(1)
				);
			}
			/*
			query.must(
					titleQ.boost(5)
					);
			*/
		}
		// end Title


		/*
		 * <content>
		 */
		// Begin Content
		//if(param.getDescription() != null && !param.getDescription().equals("")){
		if(false){
			//logger.debug("Searching Content...");
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();

			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Program.SearchContent",
						param.getDescription()
				);
				multi.should(
						whole
								.analyzer(AnalyzerNames.default_analyzer)
								.boost(AnalyzerNames.default_boosting)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getDescription(),
							"Metadata.Program.Description.DescriptionofContent",
							"Metadata.Program.Description.AudioChannelDescription"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				subquery.should(
						multi
								.boost(SearchParameters.Boosting.multi)
				);
			}
			if(isSequence){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Sequence.SearchContent",
						param.getDescription()
				);
				multi.should(
						whole
								.analyzer(AnalyzerNames.default_analyzer)
								.boost(AnalyzerNames.default_boosting)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getDescription(),
							"Metadata.Sequence.Description.DescriptionofContent",
							"Metadata.Sequence.Description.NaturalSound"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				subquery.should(
						multi
								.boost(SearchParameters.Boosting.multi)
				);
			}
			if(isScene) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Scene.SearchContent",
						param.getDescription()
				);
				multi.should(
						whole
								.analyzer(AnalyzerNames.default_analyzer)
								.boost(AnalyzerNames.default_boosting)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getDescription(),
							"Metadata.Scene.Description.DescriptionofContent",
							"Metadata.Scene.Description.NaturalSound"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				subquery.should(
						multi
								.boost(SearchParameters.Boosting.multi)
				);
			}
			if(isShot) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Shot.SearchContent",
						param.getDescription()
				);
				multi.should(
						whole
								.analyzer(AnalyzerNames.default_analyzer)
								.boost(AnalyzerNames.default_boosting)
				);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getDescription(),
							"Metadata.Shot.Description.DescriptionofContent^3.0f",
							"Metadata.Shot.Description.ShootingPlace",
							"Metadata.Shot.Description.CameraMotion",
							"Metadata.Shot.Description.SceneRange",
							"Metadata.Shot.Description.CameraAngle",
							"Metadata.Shot.Description.NaturalSound^2.0f"
					);
					multi.should(
							base
									.analyzer(AnalyzerNames.analyzers[i])
									.boost(AnalyzerNames.boosting[i])
					);
				}
				subquery.should(
						multi
								.boost(SearchParameters.Boosting.multi)
				);
			}
			/*
			query.must(
					subquery
					.boost(Boosting.subquery)
					);
			*/
		}
		// end Description

		//****************************************//
		
		/*
		 * <source>
		 * 1.14,2.13
		 */
		if(param.getSource() != null && !param.getSource().equals("") ) {
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();
			
			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MatchQueryBuilder base = QueryBuilders.matchQuery(
							"Metadata.Program.SearchSource",
							param.getSource()
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
				
			}
			if(isSequence) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MatchQueryBuilder base = QueryBuilders.matchQuery(
							"Metadata.Sequence.SearchSource",
							param.getSource()
							);
					subquery.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			query.must(
					subquery
							.boost(SearchParameters.Boosting.subquery)
			);
		}
		
		/*
		 * <meidiaform>
		 */
		// begin meidiaform
		if(param.getMediaform() != null && !param.getMediaform().equals("")){
			//logger.debug("Searching Type...");
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();

			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Program.SearchType",
						param.getMediaform()
						);
				multi.should(
						whole	
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getMediaform(),
							"Metadata.Program.Type.ProgramType",
							"Metadata.Program.Type.ProgramForm",
							"Metadata.Program.Description.VersionDescription"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}
			if(isSequence){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getMediaform(),
							"Metadata.Sequence.Type.ProgramForm"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			query.must(
					subquery
							.boost(SearchParameters.Boosting.subquery)
			);
		}
		//end Type
		
		
		/*
		 * <subject>
		 * 内容分类
		 * 内容主题
		 * 污染治理  就业  篮球  日本  等等
		 */
		// Begin Subject
		if(param.getSubject() != null && !param.getSubject().equals("")){
			//logger.debug("Searching Subject...");
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();

			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Program.SearchSubject",
						param.getSubject()
						);
				multi.should(
						whole	
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getSubject(),
							"Metadata.Program.Subject.SearchClass^3.0f",
							"Metadata.Program.Subject.Keyword^2.0f",
							"Metadata.Program.Subject.SubjectTerm^2.0f"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}
			if(isSequence){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Sequence.SearchSubject",
						param.getSubject()
						);
				multi.should(
						whole
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getSubject(),
							"Metadata.Sequence.Subject.SearchClasst3.0f",
							"Metadata.Sequence.Subject.SubjectTerm^2.0f",
							"Metadata.Sequence.Subject.Keyword^2.0f"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}
			if(isScene) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Scene.SearchSubject",
						param.getSubject()
						);
				multi.should(
						whole
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getSubject(),
							"Metadata.Scene.Subject.SubjectTerm",
							"Metadata.Scene.Subject.Keyword"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}
			if(isShot) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Shot.SearchSubject",
						param.getSubject()
						);
				multi.should(
						whole
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getSubject(),
							"Metadata.Shot.Subject.SubjectTerm",
							"Metadata.Shot.Subject.Keyword"
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			query.must(
					subquery
							.boost(SearchParameters.Boosting.subquery)
			);
		}
		// end Subject
		

		/*
		 * <region>
		 */
		if(param.getRegion() != null && !param.getRegion().equals("")){
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();
			if(isProgram) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++) {
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getRegion(),
							"Metadata.Program.Publisher.SearchDescriptionofPublisher",  //1.6.1
							"Metadata.Program.Publisher.SearchDescriptionofProducer",   //1.6.2
							"Metadata.Program.SearchCoverage"  //1.13
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}
			if(isSequence) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++) {
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getRegion(),
							"Metadata.Sequence.Publisher.DescriptionofPublisher",  //2.6.1
							"Metadata.Sequence.Publisher.DescriptionofProducer",   //2.6.2
							"Metadata.Sequence.SearchCoverage"  //2.12
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);			
			}
			if(isShot){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++) {
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getRegion(),
							"Metadata.Shot.Description.ShootingPlace"  //4.3.2
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			query.must(
					subquery
							.boost(SearchParameters.Boosting.subquery)
			);
		}

		/*
		 * <mediasize>
		 */
		if(param.getMediasize() != null && !param.getMediasize().equals("")){
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();
			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Program.SearchFormat",  //1.11
						param.getMediasize()
						);
				multi.must(
						whole
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++) {
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getMediasize(),
							"Metadata.Program.Format.AudioQuality",  //1.11.6
							"Metadata.Program.Format.VideoQuality"   //1.11.7
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			query.must(  //must or should
					subquery
			);
		}
		// end Mediasize
		
		
		/*
		 * <format>
		 * 到 VideoPath中搜索
		 */
		// begin Format
		if(param.getFormat() != null && !param.getFormat().equals("")){
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();
			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				MatchQueryBuilder whole = QueryBuilders.matchQuery(
						"Metadata.Program.SearchFormat",  //1.11
						param.getFormat()
						);
				multi.must(
						whole
						.analyzer(AnalyzerNames.default_analyzer)
						.boost(AnalyzerNames.default_boosting)
						);
				for(int i = 0; i < AnalyzerNames.counter; i++) {
					MultiMatchQueryBuilder base = QueryBuilders.multiMatchQuery(
							param.getFormat(),
							"Metadata.Program.Format.FileFormat"  //1.11.18
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}
				subquery.should(
						multi
						.boost(SearchParameters.Boosting.multi)
						);
			}

			// VideoPath
			/*
			 * 如果VideoPath没有被 AnalyzerNames,
			 * 使用字符串  子串匹配 更合适？
			 */
			MatchQueryBuilder path = QueryBuilders.matchQuery(
					"Metadata.VideoPath",
					param.getFormat()
			);
			subquery.should(path);


			query.must(  //must or should
					subquery
			);
		}
		// end Format
		

		/*
		 *  <copyright>
		 */
		// begin Copyright
		if(param.getCopyright()!=null && !param.getCopyright().equals("")){
			BoolQueryBuilder subquery = QueryBuilders.boolQuery();
			if(isProgram){
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MatchQueryBuilder base = QueryBuilders.matchQuery(
							"Metadata.Program.SearchCopyright",  //1.7
							param.getCopyright()
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}   
				subquery.should(
						multi
						);
			}
			if(isSequence) {
				BoolQueryBuilder multi = QueryBuilders.boolQuery();
				for(int i = 0; i < AnalyzerNames.counter; i++){
					MatchQueryBuilder base = QueryBuilders.matchQuery(
							"Metadata.Sequence.SearchCopyright",  //2.7
							param.getCopyright()
							);
					multi.should(
							base
							.analyzer(AnalyzerNames.analyzers[i])
							.boost(AnalyzerNames.boosting[i])
							);
				}   
				subquery.should(
						multi
						);
			}

			query.must(
					subquery
			);
		}
		//end Copyright
		

		//****************************************************************************//
		/*
		   * <filter>
		   * Filter 部分
		   * 用一些Exact Value 缩小结果范围
		*/
		AndQueryBuilder filter = QueryBuilders.andQuery();
		//AndFilter or OrFilter ???


		/*
		 *  <shotdate>
		 */
		if(param.getShotdatefrom() != null || param.getShotdateto()!=null) {
			OrQueryBuilder subfilter = QueryBuilders.orQuery();
			if(isProgram){
				RangeQueryBuilder base = QueryBuilders.rangeQuery("Metadata.Program.Date.ProducedDate");
				if(param.getShotdatefrom() != null)
					base.gte(
							FormatOfDate.formatWithTime.format(param.getShotdatefrom())
							);
				if(param.getShotdateto() != null)
					base.lte(
							FormatOfDate.formatWithTime.format(param.getShotdateto())
							);
				subfilter.add(base);
			}
			if(isShot){
				RangeQueryBuilder base = QueryBuilders.rangeQuery("Metadata.Shot.Date.ShootingDate");
				if(param.getShotdatefrom() != null)
					base.gte(
							FormatOfDate.formatWithTime.format(param.getShotdatefrom())
							);
				if(param.getShotdateto() != null)
					base.lte(
							FormatOfDate.formatWithTime.format(param.getShotdateto())
							);
				subfilter.add(base);
			}
			filter.add(subfilter);
		}
		// </shotdate>

		/*
		 * <mediatype>
		 */
		if(param.getMediatype() == null || param.getMediatype().equals("")){
			param.setMediatype("video");
		}
		else if(!param.getMediatype().equals("video")  && !param.getMediatype().equals("audio")
				&& !param.getMediatype().equals("picture")){
			param.setMediatype("video");
		}
		else{
			/*
			 * 元数据中没有相关域，有待添加
			 */
			param.setMediatype("video");
		}
		
		
		//****************************************************************************//
		/* Query + Filter
		* FilteredQuery
		*/
		FilteredQueryBuilder filteredQuery = QueryBuilders.filteredQuery(
				query,
				filter
				);
		//****************************************************************************//
		/*
		* from 
		* size
		*/
		Integer from = param.getFrom();
		Integer size = param.getSize();
		
		/*
		 * sorttype
		 * 1）关于时间排序，需要添加一个上传时间的域
		 * 2）关于热度排序。需要添加一个点击数量的域
		 */
		if(param.getSorttype() == null || param.getSorttype().equals("")){
			param.setSorttype("relevancy");
		}
		else if(!param.getSorttype().equals("relevancy")
				&& !param.getSorttype().equals("earliest")  && !param.getSorttype().equals("latest")){
			param.setSorttype("relevancy");
		}
		String sortfield = "_score";
		SortOrder order = SortOrder.DESC;
		if(param.getSorttype().equals("relevancy")){
			sortfield = "_score";
			order = SortOrder.DESC;
		}
		else if(param.getSorttype().equals("earliest")){
			sortfield = "Metadata.UploadDate";
			order = SortOrder.ASC;
		}
		else if(param.getSorttype().equals("latest")){
			sortfield = "Metadata.UploadDate";
			order = SortOrder.DESC;
		}
		/*
		 * 还没有时间相关属性，先使用默认
		 */
		sortfield = "_score";
		order = SortOrder.DESC;
		SortBuilder sort = new FieldSortBuilder(sortfield).order(order).ignoreUnmapped(true);

		/*
		* explain
		 */
		Boolean is_explain = EncodeDecode.is_explain(param.getExplain());

		/*
		* dsl
		 */
		Boolean is_dsl = EncodeDecode.is_dsl(param.getDsl());
		if(is_dsl == true){
			SearchRequestBuilder request = this.elasticsearchDao.genSearchRequset(filteredQuery, from, size ,sort, levels,is_explain);
			return request.toString();
		}

		
		//****************************************************************************//
		
		SearchResponse response = elasticsearchDao.search(filteredQuery, from, size ,sort, levels,is_explain);

		//return response.toString();
		
		return JsonFormatter.formatSearchResponse(response);
	}


}

